/*
* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "engine/include/service_adapter.h"
#include <map>
#include <stdio.h>
#include "protocol/retcode_inner/infer_retcode_inner.h"
#include "engine/include/i_async_task_manager.h"
#include "engine/include/i_engine_manager.h"
#include "engine/include/i_sync_task_manager.h"
#include "utils/infer_guard.h"
#include "utils/log/infer_log.h"

namespace OHOS {
namespace AI_STD {
namespace {
const unsigned int ADAPT_ID_BIT = 32U;
//const int INPUT_LENGTH_NULL = 0;
}
constexpr int STARTING_CLIENT_ID = 1;
constexpr int MAX_NUM_CLIENTS = 1024;

using ServiceAdapters = std::map<int, ServiceAdapter*>;
ServiceAdapters g_serviceAdapters;
std::atomic<int> g_clientIdAtomic(0);
std::mutex g_serverAdapterMutex;
std::mutex g_connectMutex;

int FindValidClientId()
{
    if (g_serviceAdapters.size() > MAX_NUM_CLIENTS) {
        HILOGE("[ServiceAdapter]Num of valid clients reaches max.");
        return INVALID_CLIENT_ID;
    }

    do {
        ++g_clientIdAtomic;
        if (g_clientIdAtomic < STARTING_CLIENT_ID) {
            HILOGI("[ServiceAdapter]Client id reaches max, reset to starting value [%d].", STARTING_CLIENT_ID);
            g_clientIdAtomic = STARTING_CLIENT_ID;
        }
    } while (g_serviceAdapters.find(g_clientIdAtomic) != g_serviceAdapters.end());

    return g_clientIdAtomic;
}

int AllocateClientAdapter()
{
    ServiceAdapter *adapter = nullptr;
    AI_NEW_INSTANCE(adapter, ServiceAdapter(g_clientIdAtomic));
    if (adapter == nullptr) {
        HILOGE("[ServiceAdapter]Failed to new adapter.");
        return INVALID_CLIENT_ID;
    }

    int clientId = adapter->GetAdapterId();

    std::lock_guard<std::mutex> guard(g_serverAdapterMutex);
    g_serviceAdapters[clientId] = adapter;
    return clientId;
}


ServiceAdapter* FindAdapter(const int clientId)
{
    std::lock_guard<std::mutex> guard(g_serverAdapterMutex);
    ServiceAdapters::iterator iter = g_serviceAdapters.find(clientId);
    if (iter != g_serviceAdapters.end()) {
        return iter->second;
    }
    return nullptr;
}

int GenerateClient()
{
    HILOGI("[ServiceAdapter]Begin to call GenerateClient.");
    std::lock_guard<std::mutex> guard(g_connectMutex);

    if (FindValidClientId() == INVALID_CLIENT_ID) {
        return INVALID_CLIENT_ID;
    }

    return AllocateClientAdapter();
}

int RemoveAdapter(const ClientInfo *clientInfo)
{
    HILOGI("[ServiceAdapter]Begin to call RemoveAdapter.");
    std::lock_guard<std::mutex> guard(g_serverAdapterMutex);
    ServiceAdapters::iterator iter = g_serviceAdapters.find(clientInfo->clientId);
    if (iter == g_serviceAdapters.end()) {
        HILOGE("[ServiceAdapter]Failed to find serverAdapter for client[%d].", clientInfo->clientId);
        return RETCODE_FAILURE;
    }

    AI_DELETE_INSTANCE(iter->second);
    g_serviceAdapters.erase(iter);
    return RETCODE_SUCCESS;
}

ServiceAdapter::ServiceAdapter(int adapterId) : adapterId_(adapterId), refCount_(0)
{
}

ServiceAdapter::~ServiceAdapter()
{
    Uninitialize();
}

int ServiceAdapter::GetSessionId(long long transactionId) const
{
    return static_cast<int>(static_cast<unsigned long long>(transactionId) & TRANS_ID_MASK);
}

int ServiceAdapter::GetAdapterId() const
{
    return adapterId_;
}

void ServiceAdapter::IncRef()
{
    ++refCount_;
}

void ServiceAdapter::DecRef()
{
    refCount_--;
}

int ServiceAdapter::GetRefCount() const
{
    return refCount_;
}

void ServiceAdapter::Uninitialize()
{
    std::lock_guard<std::mutex> guard(mutex_);
    IEngineManager* engineManager = GetEngineManager();
    if (engineManager == nullptr) {
        HILOGE("[ServiceAdapter]Failed to get engine manager.");
        return;
    }

    DataInfo inputInfo;

    for (auto &item: transactionIds_) {
        engineManager->StopEngine(item, inputInfo);
    }
    transactionIds_.clear();
}

long long ServiceAdapter::GetTransactionId(int sessionId) const
{
    return static_cast<long long>(static_cast<unsigned long long>(adapterId_) << ADAPT_ID_BIT) + sessionId;
}

int ServiceAdapter::AsyncExecute(const ClientInfo &clientInfo, const AlgorithmInfo &algoInfo,
    const DataInfo &inputInfo)
{
    IRequest *request = nullptr;
    ConvertToRequest(clientInfo, algoInfo, inputInfo, request);
    ResGuard<IRequest> guardReq(request);

    IAsyncTaskManager *asyncTaskManager = GetAsyncTaskManager();
    if (asyncTaskManager == nullptr) {
        HILOGE("[ServiceAdapter]Get async task manager fail, ret: %d, clientId: %d, sessionId: %d, algoName: %s.",
            RETCODE_OUT_OF_MEMORY, clientInfo.clientId, clientInfo.sessionId, algoInfo.algorithmName.c_str());
        return RETCODE_OUT_OF_MEMORY;
    }

    int ret = asyncTaskManager->AsyncExecute(request);
    if (ret != RETCODE_SUCCESS) {
        HILOGE("[ServiceAdapter]Fail to get async execute request, ret is %d.", ret);
        return ret;
    }

    guardReq.Detach();
    return RETCODE_SUCCESS;
}

void ServiceAdapter::ConvertToRequest(const ClientInfo &clientInfo, const AlgorithmInfo &algoInfo,
    const DataInfo &inputInfo, IRequest *&request)
{
    request = IRequest::Create();
    if (request == nullptr) {
        HILOGE("[ServiceAdapter]Fail to create request.");
        return;
    }
    request->SetRequestId(algoInfo.requestId);
    request->SetOperationId(algoInfo.operateId);
    request->SetTransactionId(GetTransactionId(clientInfo.sessionId));
    request->SetAlgoPluginType(algoInfo.algorithmName);
    request->SetMsg(inputInfo);
    request->SetClientUid(clientInfo.clientUid);
}

int ServiceAdapter::LoadAlgorithm(long long transactionId, const AlgorithmInfo &algoInfo,
    const DataInfo &inputInfo, DataInfo &outputInfo)
{
    IEngineManager *engineManager = GetEngineManager();
    if (engineManager == nullptr) {
        HILOGE("[ServiceAdapter][transactionId:%lld]Failed to get engine manager.", transactionId);
        return RETCODE_OUT_OF_MEMORY;
    }
    int retCode = engineManager->StartEngine(transactionId, algoInfo, inputInfo, outputInfo);
    if (retCode != RETCODE_SUCCESS) {
        HILOGE("[ServiceAdapter][transactionId:%lld]Failed to load algorithm.", transactionId);
        return retCode;
    }

    SaveTransaction(transactionId);
    return RETCODE_SUCCESS;
}

int ServiceAdapter::SetOption(long long transactionId, int optionType, const DataInfo &dataInfo)
{
    IEngineManager *engineManager = GetEngineManager();
    if (engineManager == nullptr) {
        HILOGE("[ServiceAdapter][transactionId:0x%llx]Failed to get engine manager.", transactionId);
        return RETCODE_OUT_OF_MEMORY;
    }

    return engineManager->SetOption(transactionId, optionType, dataInfo);
}

int ServiceAdapter::GetOption(long long transactionId, int optionType, const DataInfo &dataInfo,
    DataInfo &outputInfo)
{
    IEngineManager *engineManager = GetEngineManager();
    if (engineManager == nullptr) {
        HILOGE("[ServiceAdapter][transactionId:0x%llx]Failed to get engine manager.", transactionId);
        return RETCODE_OUT_OF_MEMORY;
    }

    return engineManager->GetOption(transactionId, optionType, dataInfo, outputInfo);
}

void ServiceAdapter::SaveTransaction(long long transactionId)
{
    std::lock_guard<std::mutex> guard(mutex_);
    transactionIds_.insert(transactionId);
}

int ServiceAdapter::UnloadAlgorithm(long long transactionId, const DataInfo &inputInfo)
{
    IEngineManager* engineInstance = GetEngineManager();
    if (engineInstance == nullptr) {
        HILOGE("[ServiceAdapter][transactionId:%lld]Failed to get engine manager.", transactionId);
        return RETCODE_OUT_OF_MEMORY;
    }
    int retCode = engineInstance->StopEngine(transactionId, inputInfo);
    if (retCode != RETCODE_SUCCESS) {
        HILOGE("[ServiceAdapter][transactionId:%lld]Failed to unload algorithm.", transactionId);
        return retCode;
    }
    RemoveTransaction(transactionId);
    return RETCODE_SUCCESS;
}

void ServiceAdapter::RemoveTransaction(long long transactionId)
{
    std::lock_guard<std::mutex> guard(mutex_);
    transactionIds_.erase(transactionId);
}

int ServiceAdapter::SyncExecute(const ClientInfo &clientInfo, const AlgorithmInfo &algoInfo,
    const DataInfo &inputInfo, DataInfo &outputInfo)
{
    IRequest *request = nullptr;
    ConvertToRequest(clientInfo, algoInfo, inputInfo, request);
    if (request == nullptr) {
        HILOGE("[ServiceAdapter]Fail to ConvertToRequest.");
        return RETCODE_OUT_OF_MEMORY;
    }
    ResGuard<IRequest> guardReq(request);
    ISyncTaskManager *taskMgr = GetSyncTaskManager();
    if (taskMgr == nullptr) {
        HILOGE("[ServiceAdapter]Get task manager failed, ret is %d", RETCODE_OUT_OF_MEMORY);
        return RETCODE_OUT_OF_MEMORY;
    }
    IResponse *response = nullptr;
    int retCode = taskMgr->SyncExecute(request, response);
    ResGuard<IResponse> guardRes(response);
    if ((retCode != RETCODE_SUCCESS) || (response == nullptr)) {
        HILOGE("[ServiceAdapter]Execute request failed, ret is %d", retCode);
        return retCode;
    }
    outputInfo = response->GetResult();
    response->Detach();

    LOG_INFO("After Detach outputInfo size is %d\n", outputInfo.size());
    HILOGI("[ServiceAdapter]After Detach outputInfo size is %d", outputInfo.size());

    return RETCODE_SUCCESS;
}
} // namespace AI_STD
} // namespace OHOS
